package game;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * @author Anirban Laha
 * 
 * This class is used to generate the payoff matrix from the input file.
 * The input file is assumed to be in GAMBIT format
 *
 */
public class PayoffMatrixGenerator {
    
    private File inputFile;
    
    private int numPlayers;
    
    private int[] actionList;
    
    private List<Double>[] payoffMatrix;
    
    private List<Integer>[] indexOrderList;
    
    /**
     * 
     * @param fileName
     * Constructor of PayoffMatrixGenerator
     */
    public PayoffMatrixGenerator(String fileName) {
        inputFile = new File(fileName);
    }
    
    /**
     * This method processes the file and generates the payoff matrix
     * and then rearranges the payoffMatrix according to our convenience for
     * efficient computation
     */
    public StrategicGame generate() {
        try{
            BufferedReader br = new BufferedReader(new FileReader(inputFile));
            processFile(br);
            rearrangePayoff();
            br.close();
        } catch(Exception e) {
            System.out.println("File reading error: Exception");
            e.printStackTrace();
            System.exit(0);
        }
        return new StrategicGame(numPlayers, actionList, payoffMatrix);
    }
    
    /**
     * PayoffMatrix stores the lists of utilities per player and the utilities are
     * in order of arrival. However, we need to rearrange them for easy computation of
     * equilibria. For easy computation, we need to store the utilities for different strategies 
     * of a player for a particular combination of other player strategies in contiguous locations.
     * 
     * This method takes care of the rearrangement according to the rearrangement order
     * as present in indexOrderList. The rearranged lists per player then overwrites the original
     * lists in payOffMatrix.
     */
    private void rearrangePayoff() {
        int jump = 1;
        for(int i=0; i<numPlayers; i++) {
            int nextjump = jump * actionList[i];
            List<Double> newList = new ArrayList<Double>();

            for(int x=0; x < payoffMatrix[i].size(); x+=nextjump) {
                for(int j=0; j < jump; j++) {
                    for(int k=0; k< actionList[i]; k++) {
                        int index = x + j + k * jump;
                        indexOrderList[i].add(index);
                        newList.add(payoffMatrix[i].get(index));
                    }
                }
            }
            payoffMatrix[i] = newList;
            jump = nextjump;
        }
    }

    /**
     * 
     * @param br
     * @throws IOException
     * @throws NumberFormatException
     * 
     * This method takes the buffered reader with the fileinfo which contains the game in GAMBIT
     * format and extracts the data of interest like number of players, actions of the players,
     * the list of utilities of each player
     * 
     * Special Note: PayoffMatrix contains the utilities for each player in the order of arrival
     * However, they need to be arranged for easy computation of the equilibrium strategies.
     * For easy computation, we need to store the utilities for different strategies of a player
     * for a particular combination of other player strategies in contiguous locations. So, for this
     * we need a rearrangement. So indexOrderList stores the indices of rearrangement.
     */
    @SuppressWarnings("unchecked")
    private void processFile(BufferedReader br) throws IOException, NumberFormatException {
        
        String temp, str = null;
        while((temp = br.readLine()) != null) {
            str = temp;
            List<String> list = tokenizeString(str);
            if(list.isEmpty()) {
                continue;
            }
            if("Players:".equals(list.get(0))) {
                numPlayers = Integer.parseInt(list.get(1));
                actionList = new int[numPlayers];
                payoffMatrix = new List[numPlayers];
                indexOrderList = new List[numPlayers];
                for(int i=0; i< numPlayers; i++) {
                    payoffMatrix[i] = new ArrayList<Double>();
                    indexOrderList[i] = new ArrayList<Integer>();
                }
            }
            else if("Actions:".equals(list.get(0))) {
                for(int i=0; i<list.size()-1; i++) {
                    actionList[i] = Integer.parseInt(list.get(i+1));
                }
            }
        }
        List<String> list = tokenizeString(str);
        for(int i=0; i<list.size(); i++) {
            payoffMatrix[i % numPlayers].add(Double.parseDouble(list.get(i)));
        }
    }
    
    /**
     * @param str
     * @return The list of tokens for a particular string
     */
    private List<String> tokenizeString(String str) {
        List<String> list = new ArrayList<String>();
        StringTokenizer st = new StringTokenizer(str);
        while (st.hasMoreTokens()) {
             list.add(st.nextToken());
        }
        return list;
    }
    
    /**
     * @return the indexOrderList
     */
    public List<Integer>[] getIndexOrderList() {
        return indexOrderList;
    }

}
